package com.onpositive.wikitemplate;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import storage.v1.ParserV2;

import com.onpositive.data.units.Unit;
import com.onpositive.data.units.Units;
import com.onpositive.mediawiki.crawler.GlobalTemplateExtractor;
import com.onpositive.units.ParsedDimension;
import com.onpositive.wikitemplate.WikiTemplate.TemplateProvidedPropertyMetadata;

public class WikiTemplateManager {
	
	private final String globalPath = "http://en.wikipedia.org/w/" ;
	private String localTemplateStoragePath = "C:/wikistorage/templates/" ;
	private HashMap<String,WikiTemplate> templateMap = new HashMap<String, WikiTemplate>() ;
	private HashSet<String> badNames = new HashSet<String>() ;
	
	private StringBuilder sBuilder ;
	LocalTemplateStorage localTemplateStorage = new LocalTemplateStorage() ;
	GlobalTemplateExtractor globalTemplateExtractor = new GlobalTemplateExtractor() ;
	

	public WikiTemplate getTemplate( String templateName )
	{
		templateName = templateName.replace("?", "_(query_mark)") ;
		String subPath = " ";
		String templateName_lc = templateName.toLowerCase().replaceAll("_", subPath).trim() ;
		
		WikiTemplate result = templateMap.get(templateName_lc) ;
		if( result != null )
			return result ;
		
		if( badNames.contains(templateName_lc) )
			return null ;		
		
		ArrayList<String> localPaths = new ArrayList<String>() ;
		ArrayList<String> globalSubPaths = new ArrayList<String>() ;
		
		String wholeText = getTemplateDocs(templateName, "" ) ;
		if( wholeText == null || wholeText.length() == 0 ){
			badNames.add(templateName_lc) ;
			return null ;
		}
			
		wholeText = ParserV2.killBreaks( wholeText ) ;
		globalSubPaths.add( globalTemplateExtractor.createGlobalSubPath(templateName_lc, subPath) ) ;
		localPaths.add( localTemplateStorage.createFilePath(templateName_lc, subPath) ) ;		
		
		ArrayList<String> props = new ArrayList<String>() ;		
		boolean empty = !getPropertyFromTemplateBody( props, templateName, wholeText ) ;
		if( empty )
		{
			subPath = "examples" ;
			String wholeText_example = getTemplateDocs( templateName, subPath ) ;
			wholeText_example = ParserV2.killBreaks( wholeText_example ) ;
			globalSubPaths.add( globalTemplateExtractor.createGlobalSubPath(templateName_lc, subPath) ) ;
			localPaths.add( localTemplateStorage.createFilePath(templateName_lc, subPath) ) ;
			
			empty = !getPropertyFromTemplateBody( props, templateName, wholeText_example ) ;			
		}
		
		if( empty ){
			badNames.add(templateName_lc) ;
			return null ;
		}

		result = new WikiTemplate( templateName_lc, this.globalPath, globalSubPaths, localPaths ) ;
		fillProperties( result, props ) ;
		
		String txt = eraseTemplateOccurances( wholeText , templateName ) ;
		//erase<br>, </br>
		extractComments( txt , result ) ;
		
		for( TemplateProvidedPropertyMetadata meta : result.getPropertyMap().values() ){
			
			if( meta.description == null )
				continue ;
			
//			System.out.print( meta.name ) ;
//			System.out.print( '\n' ) ;
//			System.out.print( meta.description ) ;
//			System.out.print( "\n\n" ) ;
		}
		
		templateMap.put( templateName_lc, result ) ;
		return result ;
	}
	
	private String eraseTemplateOccurances(String text, String templateName) {
		
		StringBuilder builder = new StringBuilder( text.length() ) ;
		ArrayList<Integer> bounds = getTemplateBodyBounds(templateName, text );
		
		if( bounds == null )
			return text.toLowerCase() ;

		builder.append( text.substring( 0, bounds.get(2) ) ) ;
		
		for( int i = 4 ; i < bounds.size() ; i += 4 )
			builder.append( text.substring( bounds.get( i-1 ), bounds.get( i+2 ) ) ) ;
		
		builder.append( text.substring( bounds.get( bounds.size()-1 ), text.length() ) ) ;

		return builder.toString().toLowerCase();
	}

	private void extractComments(String text_lc, WikiTemplate template )
	{
		ArrayList<Integer> frontIndexes = new ArrayList<Integer>() ;
		ArrayList<Integer>  rearIndexes = new ArrayList<Integer>() ;
		HashMap<Integer,String> positionMap = new HashMap<Integer, String>() ;
		
		analyzePropertyNamesOccurences( text_lc, template, frontIndexes, rearIndexes, positionMap );
		
		int s = rearIndexes.size();
		if( s == 0 )
			return ;
		
		int[] lineEnds = new int[ s ] ;
		String[] comments = new String[s] ;
		
		for( int i = 0 ; i < s ; i++ )
			lineEnds[i] = text_lc.indexOf( '\n', rearIndexes.get(i) ) ;		
		try{
		comments[s-1] = text_lc.substring( rearIndexes.get(s-1), lineEnds[s-1] ) ;
		}
		catch( Exception e )
		{
			e.printStackTrace() ;
		}
		for( int i = s-1 ; i > 0 ; i-- )
			if( lineEnds[i-1] != lineEnds[i] )
				comments[i-1] = text_lc.substring( rearIndexes.get(i-1), lineEnds[i-1] ) ;			
			else
				comments[i-1] = comments[i] ;
		
		for( int i = 0 ; i < s ; i++ )
		{
			if( comments[i].length() == 0 )
				continue ;
			
			Integer pos = rearIndexes.get(i);
			String propertyName = positionMap.get(pos);
			
			ArrayList<TemplateProvidedPropertyMetadata> metaList = template.getPropertiesByActualName(propertyName) ;
			if( metaList == null )
				metaList = new ArrayList<WikiTemplate.TemplateProvidedPropertyMetadata>() ;
			
			TemplateProvidedPropertyMetadata _meta = template.getPropertyByName( propertyName ) ;
			if( _meta != null )
				metaList.add(_meta) ;
			
			for( TemplateProvidedPropertyMetadata meta : metaList )
			{
				if( meta.description == null )
					meta.description = comments[i] ;
				else{
					HashSet<String> otherDescriptions = meta.otherDescriptions ;
					if( otherDescriptions == null ){
						meta.otherDescriptions = new HashSet<String>() ;
						otherDescriptions = meta.otherDescriptions ;
					}
					otherDescriptions.add( comments[i]) ;
				}
			}
		}
	}

	private void analyzePropertyNamesOccurences( String text_lc,
												 WikiTemplate template,
												 ArrayList<Integer> frontIndexes,
												 ArrayList<Integer> rearIndexes,
												 HashMap<Integer, String> positionMap )
	{
		HashSet<String> names = new HashSet<String>() ;
		ArrayList<String> allNames = new ArrayList<String>() ;
		for( TemplateProvidedPropertyMetadata mtd : template.getPropertyMap().values() )
		{
			allNames.add( mtd.name ) ;
			if ( mtd.actualName != null )
				allNames.add( mtd.actualName ) ;
		}	

//		int k = 0 ;
		for( String name : allNames )
		{			
			name = name.trim() ;
			for( int i = text_lc.indexOf( name ) ; i >= 0 ; i = text_lc.indexOf( name.trim(), i+1 ) )
			{
				char ch = text_lc.charAt(i-1);
				if( !Character.isLetterOrDigit( ch ) )
				{
//					k++ ;
					frontIndexes.add(i) ;
					rearIndexes.add( i + name.length() ) ;
					positionMap.put( i, name ) ;
				}
			}
		}
		
		int s = frontIndexes.size();
		boolean[] frontMarks = new boolean[ s ] ;
		boolean[]  rearMarks = new boolean[ s ] ;
		boolean[]  acceptedOccurances = new boolean[ s ] ;
		for( int i = 0 ; i < s ; i++ ){
			frontMarks[i] = false ;
			rearMarks[i] = false ;
			acceptedOccurances[i] = false ;
		}
		
		boolean mustContinue = true ;
		
		int acceptedOccurencesCount = 0 ;
		
		int initialPropertiesCount = names.size() ;
		while( mustContinue )
		{			
			@SuppressWarnings("unused")
			String frontHalf = extractHalfPattern(text_lc, frontIndexes, frontMarks, "front" );
			String  rearHalf = extractHalfPattern(text_lc,   rearIndexes,  rearMarks, "rear"  );
			
			for( int i = 0 ; i < s ; i++ )
				rearMarks[i] = rearMarks[i] && frontMarks[i] ;
			
			refineRearIndexes( rearHalf.length(), text_lc, rearIndexes, rearMarks ) ;
			
			for( int i = 0 ; i < s ; i++ )
				if( frontMarks[i] && rearMarks[i] ){
					acceptedOccurances[i] = true ;
					
					String n = positionMap.get( frontIndexes.get(i) ) ;
					ArrayList<TemplateProvidedPropertyMetadata> lst = template.getPropertiesByActualName(n) ;
					
					if( lst == null )
						names.remove(n) ;
					else
						for( TemplateProvidedPropertyMetadata meta : lst )
							names.remove( meta.name ) ;
				}

			int presentCount = 0 ;
			for( int i = 0 ; i < s ; i++ ){				
				presentCount += acceptedOccurances[i] ? 1 : 0 ;
				frontMarks[i] = acceptedOccurances[i] ;
				 rearMarks[i] = acceptedOccurances[i] ;
			}
			
			mustContinue = ( presentCount > acceptedOccurencesCount ) || ((double)names.size())/initialPropertiesCount > 0.1 ;
					
			acceptedOccurencesCount = presentCount ;
		}
		
		HashMap<Integer,String> bufPositionMap = new HashMap<Integer, String>() ;		
		ArrayList<Integer>  rearBuf = new ArrayList<Integer>(s) ;
		for( int i = 0 ; i < s ; i++ )
			if( acceptedOccurances[i] ){
				Integer k = rearIndexes.get(i);
				rearBuf.add(  k ) ;
				bufPositionMap.put( k , positionMap.get( frontIndexes.get(i) ) ) ;
			}		
				
		rearIndexes.clear() ;
		for( Integer i : rearBuf )
			rearIndexes.add(i) ;
		
		Collections.sort( rearIndexes ) ;
				
		positionMap.clear() ;
		positionMap.putAll( bufPositionMap ) ;
				
		return ;
	}

	private void refineRearIndexes( int rearHalfLength, String text_lc,	ArrayList<Integer> rearIndexes, boolean[] rearMarks )
	{
		int s = rearMarks.length ;
		for( int i = 0 ; i < s ; i++ )
		{
			if( !rearMarks[i] )
				continue ;
			
			int k = rearIndexes.get(i) ;
			int ind ;
			//for( ; text_lc.charAt(k) == ' ' ; k++ ) ;
			
			for( int j = 0 ; j < rearHalfLength ; j++ ){
//				{
//					ind = text_lc.indexOf('\n',k) ;
//					System.out.print( text_lc.substring(k,ind+1) ) ; 
//				}
				
				for( ; text_lc.charAt(k) == ' ' ; k++ ) ;			
				k++ ;
//				{
//					ind = text_lc.indexOf('\n',k) ;
//					System.out.print( text_lc.substring(k,ind+1) ) ; 
//				}
			}
//			{
//				ind = text_lc.indexOf('\n',k) ;
//				System.out.print( text_lc.substring(k,ind+1) ) ; 
//			}
			
			for( char ch = text_lc.charAt(k) ; !( ch == '\n' || Character.isLetterOrDigit( ch ) ); ch = text_lc.charAt(++k) );
			
//			{
//				ind = text_lc.indexOf('\n',k) ;
//				System.out.print( text_lc.substring(k,ind+1) ) ; 
//			}
			
			rearIndexes.set( i, k ) ;
		}
	}

	private String extractHalfPattern( String text_lc, ArrayList<Integer> indexes, boolean marks[], String place )
	{
		boolean isFront = place.equals("front");
		
		Estimator estimator = new Estimator() ;
		StringBuilder builder = new StringBuilder() ;
		HashMap<Character, Integer> charMap = new HashMap<Character, Integer>() ;
		boolean[] depricatedOccurances = new boolean[ marks.length ] ;
		for( int i = 0 ; i < depricatedOccurances.length ; i++ )
			depricatedOccurances[i] = marks[i] ;
		
		boolean mayContinue = true ;
		
				
		int initialShift = isFront ? -1 : 0 ;
		int[] shifts = new int[ indexes.size() ] ;
		for( int i = 0 ; i < shifts.length ; i++ )
			shifts[i] = initialShift ;
		
		int incr  = isFront ? -1 : 1 ;
		while ( mayContinue )
		{
			charMap.clear() ;
			for( int k = 0 ; k < indexes.size() ; k++ )
			{				
				if( depricatedOccurances[k] )
					continue ;
				
				int i = indexes.get(k) ;
				int shift = shifts[k] ;
				Character ch ;
				
				for( ch=text_lc.charAt( i+shift ) ; ch == ' ' ;)//Character.isWhitespace(ch);  )
				{
					shift += incr ;
					ch = text_lc.charAt( i+shift ) ;
				}			
				shifts[k] = shift ;
				
				Integer count = charMap.get(ch) ;
				if( count == null )
					charMap.put( ch, 1 ) ;
				else
					charMap.put( ch, count + 1 ) ;				
			}
			
			char ch = estimator.decide(charMap) ;
			if( ch != 0 )
			{
				builder.append(ch) ;
				for( int k = 0 ; k < indexes.size() ; k++ )
				{				
					if( depricatedOccurances[k] )
						continue ;
					
					int shift = shifts[k] ;
					shifts[k] += incr ;
					int i = indexes.get(k) ;					
					if ( text_lc.charAt( i+shift ) != ch )
						depricatedOccurances[k] = true ;					
				}								
			}
			else
				mayContinue = false ;			
		}
		for( int i = 0 ; i < depricatedOccurances.length ; i++ ){
			marks[i] = !depricatedOccurances[i] ;
			//marks[i] = depricatedOccurances[i] ? marks[i] : true ;
		}
		
		if( isFront )
		{
			StringBuilder inverse = new StringBuilder( builder.length() ) ;
			for( int i = builder.length() ; i > 0 ; i-- )
				inverse.append( builder.charAt(i-1) ) ;
			
			return inverse.toString() ;
		}
		else
			return builder.toString() ; 
	}
	
	static class Estimator{
		
		private final double criticalRatio = 0.8 ;
		//private ArrayList<Double> factors = new ArrayList<Double>() ;
		private boolean gotIntoPlace = false ; 
		char decide( Map<Character,Integer> charMap )
		{
			int maxCount = -1 ;
			int totalCount = 0 ;
			char leadingChar = 0 ;
			for( Entry<Character,Integer> entry : charMap.entrySet() )
			{
				int count = entry.getValue() ;
				totalCount += count ;
				if( count > maxCount ){
					maxCount = count ;
					leadingChar = entry.getKey() ;
				}
			}
			double currentRatio = ((double)maxCount) / totalCount;
			//factors.add( currentRatio  ) ;
			
			if( gotIntoPlace ){
				if( currentRatio < criticalRatio )
					return 0 ;
			}
			else
				gotIntoPlace = currentRatio > criticalRatio ;
				
			return leadingChar != '\n' ? leadingChar : 0 ;
		}
	}

	static class TemporaryMeta{
		
		protected String name = null ;
		protected String actualName = null ;
		protected HashSet<String> inBodyDescription = new HashSet<String>() ;
		//private String description = null ;
		protected HashSet<String> values  = new HashSet<String>() ;
		protected HashSet<String> parents = new HashSet<String>() ;
		protected HashSet<String> children = new HashSet<String>() ;
	}
	
	private void fillProperties( WikiTemplate template, ArrayList<String> props) {
		
		HashMap<String,TemporaryMeta> map = prepareMap(props);
		
		for( Entry<String,TemporaryMeta> entry : map.entrySet() )
		{
			WikiTemplate.TemplateProvidedPropertyMetadata meta = createMeta( entry.getValue() ) ;
			template.addMeta( meta ) ;
		}
	}

	private TemplateProvidedPropertyMetadata createMeta( TemporaryMeta tMeta)
	{
		TemplateProvidedPropertyMetadata result = new TemplateProvidedPropertyMetadata() ;
		
		result.name = tMeta.name ;
		//result.actualName = tMeta.name ;
		if( tMeta.inBodyDescription.size() != 0 )
			result.inBodyDescription = tMeta.inBodyDescription.iterator().next() ;

		result.children = tMeta.children ;
		result.parents = tMeta.parents ;
		
		int ind = result.name.lastIndexOf(" ");
		String suffix = result.name.substring( ind + 1).trim() ;
		suffix = Units.adjustUnits(suffix) ;
		Unit unit = Units.getUnitByShortName( suffix, null ) ;
		if ( unit != null )
		{
			result.actualName = result.name.substring( 0, ind ).trim() ;
			result.type = ParsedDimension.ID ;
			result.primaryUnit = unit.getPrimaryUnit() ;
		}
		
		return result ;
	}

	private HashMap<String,TemporaryMeta> prepareMap(ArrayList<String> props)
	{
		HashMap<String,TemporaryMeta> map = new HashMap<String, WikiTemplateManager.TemporaryMeta>() ;
		
		TemporaryMeta currentParent = null ;
		for( int i = 0 ; i < props.size() ; i += 3 )
		{
			String str = props.get(i) ;
			
			if( str == null ){
				currentParent = null ;
				continue ;
			}
			
			str = props.get(i).toLowerCase().trim() ;
			
			boolean isSuperProperty = str.startsWith("<!--");
			String pName = !isSuperProperty ? str : str.substring(4).trim() ;
			
			TemporaryMeta meta = map.get( pName ) ;
			if( meta == null )
			{
				meta = new TemporaryMeta() ;
				map.put( pName , meta ) ;
				meta.name = pName ;
			}
			
			if( isSuperProperty )
				currentParent = meta ;

			else if( currentParent != null ){				
				currentParent.children.add( pName ) ;
				meta.parents.add( currentParent.name ) ;
			}
			
			String value = props.get(i+1) ;
			String comment = props.get(i+2) ;
			
			if( value != null )
				meta.values.add( value ) ;
			
			if( comment != null )
				meta.inBodyDescription.add( comment ) ;						
		}
		
		return map ;
	}

	boolean getPropertyFromTemplateBody(ArrayList<String> propertyDefinition, String templateName, String text )
	{
		int initialSize = propertyDefinition.size() ;
		
		if( text.trim().length() == 0 )
			return false ;
		
		String text_lc = text.toLowerCase() ;
		ArrayList<Integer> bounds = getTemplateBodyBounds(templateName, text_lc );
		if( bounds == null )
			return false ;
		
		//ArrayList<String> propertyDefinition = new ArrayList<String>() ;		
		for( int i = 0 ; i < bounds.size() ; i += 4 )
		{
			Integer start = bounds.get(i);
			Integer end = bounds.get(i+1);
			String currentSubstring = text_lc.substring( start, end );
			ArrayList<Integer> marks = markSuperProperties( currentSubstring , start ) ;
			if( marks == null )
				continue ;
			
			int i1 = marks.get(0) ; 
			int i2 = marks.get(1) ;
			extractPropertyDefinitions( propertyDefinition, text_lc.substring( i1, i2 ) ) ;
			
			for( int j = 2 ; j < marks.size() ; j += 2 )
			{
				propertyDefinition.add( text_lc.substring( marks.get(j-1), marks.get(j) ) ) ;//strtsWith "<!--"
				propertyDefinition.add( null ) ; //empty value
				propertyDefinition.add( null ) ; //empty comment
				
				i1 = marks.get(j) + 3;
				i2 = marks.get(j+1) ;				
				extractPropertyDefinitions( propertyDefinition, text_lc.substring( i1, i2 ) ) ;				
			}
			
			propertyDefinition.add( null ) ; // indicate template occurance end
			propertyDefinition.add( null ) ; //empty value
			propertyDefinition.add( null ) ; //empty comment

			//debug purpose
//			{
//				for( int j = 1 ; j < marks.size()-1 ; j+= 2 ) 
//					System.out.print( text_lc.substring( marks.get(j), marks.get(j+1) + 3 ) + "\n" ) ;
//				
//				System.out.print( "======\n" ) ;
//			}
		}		
		return propertyDefinition.size() > initialSize + 3  ;
	}

	private void extractPropertyDefinitions( ArrayList<String> defs, String text_lc )
	{
		String[] lines = text_lc.split("\n") ;
		
		for( int i = 0 ; i < lines.length ; i++ )
		{
			String currentLine = lines[i];
			int ind = currentLine.indexOf('=') ;
			if( ind < 0  )
				continue ;
			
			String propertyName = currentLine.substring(0,ind).replace( "|", "" ).trim();
			defs.add( propertyName ) ;
			
			String ending = currentLine.substring( ind + 1).trim() ;
			if( ending.length() == 0 ){
				defs.add( null ) ; // empty value
				defs.add( null ) ; // empty comment
				continue ;
			}
			
			int ind1 = ending.indexOf("<!--") ;
			
			if( ind1 < 0 ){
				defs.add( ending.trim() ) ; // value
				defs.add( null ) ;		 // empty comment				
			}
			else{
				String value = ind1 > 0 ? ending.substring( 0, ind1 ).trim() : null ;
				
				int ind2 = ending.indexOf("-->", ind1 ) ;
				
				String comment = null ;
				if( ind2 >= 0 ){
					comment = ending.substring( ind1 + 4, ind2 ).trim() ;
					value = ending.substring(ind2 + 3).trim() ;
					value = value.length() != 0 ? value : null ; 
				}
				else{
					StringBuilder builder = new StringBuilder() ;
					builder.append( ending.substring( ind1 + 4 ).trim() ) ;
					
					while( ++i < lines.length )
					{
						currentLine = lines[i] ;
						ind2 = currentLine.indexOf( "-->" ) ;
						if( ind2 < 0 ){
							builder.append( " " ) ;
							builder.append( currentLine ) ;
						}							
						else{
							builder.append( currentLine.substring(0, ind2).trim() ) ;
							break ;
						}
					}
					comment = builder.toString().trim() ;
				}				
				
				defs.add( value )   ;
				defs.add( comment ) ;
			}						
		}
		System.out.println( this.getClass().toString()+": prop def extracted") ;
		
	}

	private ArrayList<Integer> markSuperProperties( String text_lc, int start )
	{
		ArrayList<Integer> result = new ArrayList<Integer>() ;
		result.add( 0 + start ) ;
		
		int l = text_lc.length();
		int i1 = 0, i2 = 0 ;
		while( true ){
			
			i1 = text_lc.indexOf( "<!--", i2 ) ;
			if( i1 < 0 )
				break ;
			
			i2 = text_lc.indexOf( "-->", i1 ) ;
			if( i2 < 0 )
				return null ;
			
			int k = text_lc.lastIndexOf("<!--", i2 ) ;
			if( k != i1 )
				return null ;// invalid comment
			
			boolean goodStart = false ;
			if( i1 > 0 )
			{				 
				
				for( int i = i1-1 ; Character.isWhitespace( text_lc.charAt(i) ) && i >= 0 ; i-- )
					if( text_lc.charAt(i) == '\n' ){
						goodStart = true ;
						break ;
					}
			}
			else
				goodStart = true ;
			
			if( !goodStart )
				continue ;
			
			boolean goodEnd = false ;
			if( i2 + 3 < l )
			{				 
				
				for( int i = i2 + 3 ; Character.isWhitespace( text_lc.charAt(i) ) && i < l ; i++ )
					if( text_lc.charAt(i) == '\n' ){
						goodEnd = true ;
						break ;
					}
			}
			else
				goodEnd = true ;
			
			if( goodStart && goodEnd )
			{
				result.add( i1 + start ) ;
				result.add(	i2 + start ) ;
			}
		}

		result.add( l + start ) ;
		return result ;
	}

	// template occurences appear to be between bounds[4*i] and bounds[4*i+1] for each i.
	// the externality is in [0, bounds[2]],[ bounds[4*i + 3] , bounds[4*(i+1)+2] ]for each i, [bounds[bounds.size-1], textLength]
	private ArrayList<Integer> getTemplateBodyBounds(String templateName, String text_lc)
	{
		String name_lc = templateName.toLowerCase().trim() ;
		
		ArrayList<Integer> bounds = new ArrayList<Integer>() ;
		
		ArrayList<Integer> bracketsPos = new ArrayList<Integer>() ;
		BitSet weights = new BitSet() ;
		for( int i = text_lc.indexOf("{{" ) ; i >= 0 ; i = text_lc.indexOf("{{", i )  )
		{
			bracketsPos.add(i) ;
			weights.set(i++) ;
		}
		
		if( bracketsPos.size() == 0 )
			return null ;
		
		for( int i = text_lc.indexOf("}}" ) ; i >= 0 ; i = text_lc.indexOf("}}", i )  )
			bracketsPos.add(i++) ;
		
		Collections.sort( bracketsPos ) ;
		
		int startInd = 0 ;
		int endInd = 0 ;
		int nameInd = 0 ;
		
		while( startInd < text_lc.length() && startInd >= 0 ){
			
			nameInd = text_lc.indexOf( name_lc, endInd ) ;
			if( nameInd < 0 )
				break ;
			
			endInd = nameInd+name_lc.length() ;
			
			startInd = text_lc.lastIndexOf("{{", nameInd ) ;
			if( startInd < 0 )
				continue ;
			
			if( text_lc.substring( startInd+2, nameInd).trim().length() != 0	)
				continue ;
			
			int i = bracketsPos.indexOf( startInd ) + 1 ;
			for( int w = 1 ; w > 0 ; i++ )
				w += weights.get( bracketsPos.get(i) ) ? 1 : -1 ;

			endInd = bracketsPos.get(i-1) ;
			//if( text_lc.substring(na) )
			bounds.add( nameInd + name_lc.length() ) ;
			bounds.add( endInd ) ;
			
			bounds.add( startInd ) ;
			bounds.add( endInd + 2 ) ;
			
			endInd += 2 ;							
		}
		
		//for debug purpose
//		for( int i = 0 ; i < bounds.size() ;  )
//		{
//			String str = text_lc.substring(bounds.get(i), bounds.get(i+1) ) ;
//			System.out.print(str + "\n====================\n") ;
//			i += 2 ;
//		}
		ArrayList<Integer> _bounds = new ArrayList<Integer>() ;
		for( int i = 0 ; i < bounds.size() ; i+=2 )
			if( text_lc.substring(bounds.get(i), bounds.get(i+1)).contains("\n") )
			{
				_bounds.add(bounds.get(i)) ;
				_bounds.add(bounds.get(i+1)) ;
			}
		return _bounds.size() > 0 ? _bounds : null ;
	}

	public String getTemplateDocs( String _templateName, String _subPath )
	{
		String templateName = _templateName.toLowerCase() ;
		String subPath = _subPath.toLowerCase() ;
		
		String result = localTemplateStorage.load( templateName, subPath );
		if( result == null )
		{
			result = globalTemplateExtractor.loadTemplateDocs(templateName, subPath ) ;
			if( result != null )
				localTemplateStorage.saveToLocalStorage( templateName, subPath, result ) ;
		}
		
		return result ;
	}
}
